﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace Model
{
    public class Bedrag : Algoritme
    {
        private object stop;

        private static Bedrag algoritme;

        private Bedrag()
        {
            stop = true;
        }

        public Winkelwagen antwoord;

        public static Bedrag GetInstance()
        {
            if (algoritme == null)
            {
                algoritme = new Bedrag();
            }
            return algoritme;
        }

        public override void StopAlgoritme()
        {
            lock (stop)
            {
                stop = !((bool)stop);
            }
        }

        public override Winkelwagen AlgoritmeUitvoeren()
        {
            return VolgInstructies();
        }

        /// <summary>
        /// Dit deel voert het voorgestelde algoritme van meneer Beumers/Bemelmans uit
        /// </summary>
        /// <returns></returns>
        private Winkelwagen VolgInstructies()
        {
            this.antwoord = new Winkelwagen(LosOp());
            return antwoord;
        }


        private Winkelwagen LosOp()
        {
            Winkel w = Winkel.GetInstance();
            stop = false;
            List<Oplossing> S = new List<Oplossing>();
            S.Add(new Oplossing()); //voegt een lege oplossing toe, deze oplossing is het begin
            Oplossing h = new Oplossing();
            long bestetijd = Rekenen.berekentijd(h.Kar);
            while (!(bool)stop && S.Count != 0)
            {
                Oplossing k = new Oplossing(S[0].Toestand, new Winkelwagen(S[0].Kar));
                S.RemoveAt(0);
                if (Rekenen.berekentijd(k.Kar) < bestetijd || (Rekenen.berekentijd(k.Kar) == bestetijd && k.Kar.Totaalprijs > h.Kar.Totaalprijs))
                {
                    h = new Oplossing(k.Toestand, new Winkelwagen(k.Kar));
                    bestetijd = Rekenen.berekentijd(h.Kar);
                    w.OplossingKlaar();
                }
                S.InsertRange(0, k.kindertjes(bestetijd));
            }
            return h.Kar;
        }

        public class Rekenen
        {
            public static long berekentijd(Winkelwagen wagen)
            {
                double bedrag = AbstracteLaag.GetInstance().Doelbedrag;
                double factor = AbstracteLaag.GetInstance().Factor;
                double snelheid = AbstracteLaag.GetInstance().Snelheid;
                long tijd = Convert.ToInt64(wagen.Afstand / snelheid);
                tijd = Convert.ToInt64(tijd + ((Math.Abs(wagen.Totaalprijs - bedrag)) / factor));
                return tijd;
            }
        }

         private class Oplossing
        {
            private List<POI> toestand;
            private Winkelwagen kar;

            public Oplossing()
            {
                toestand = new List<POI>();
                Toestand.AddRange(AbstracteLaag.GetInstance().PuntenVanInteresse);
                Kar = new Winkelwagen(new POI(new Point(AbstracteLaag.GetInstance().PuntenVanInteresse[0].Plaats.X, AbstracteLaag.GetInstance().PuntenVanInteresse[0].Plaats.Y), 0));
                foreach (Kind k in AbstracteLaag.GetInstance().PuntenVanInteresse[0].Kinderen)
                {
                    Kar.Plaats.VoegKindToe(new Kind(k.Nummer, k.Afstand));
                }
                Toestand[0].Doorlopen = false;
            }

            public Oplossing(List<POI> toestand, Winkelwagen kar)
            {
                this.toestand = new List<POI>();
                this.Toestand.AddRange(toestand);
                this.kar = new Winkelwagen(kar);
            }


            public List<Oplossing> kindertjes(long beste)
            {
                List<Oplossing> antwoord = new List<Oplossing>();
                if (beste >= this.kar.Afstand / AbstracteLaag.GetInstance().Snelheid)
                {
                    //maak een poi met alle eigenschappen van zijn origineel
                    POI poi = new POI(AbstracteLaag.GetInstance().PuntenVanInteresse[kar.Plaats.Nummer].Plaats, AbstracteLaag.GetInstance().PuntenVanInteresse[kar.Plaats.Nummer].Nummer);
                    for (int i = 0; i < AbstracteLaag.GetInstance().PuntenVanInteresse[kar.Plaats.Nummer].Artikelen.Count; i++)
                    {
                        poi.Artikelen.Add(AbstracteLaag.GetInstance().PuntenVanInteresse[kar.Plaats.Nummer].Artikelen[i]);
                    }
                    //haal hier alle ongewenste producten uit
                    for (int i = 0; i < poi.Artikelen.Count; i++)
                    {
                        if (AbstracteLaag.GetInstance().Categoriën.Count != 0)
                        {
                            if ((AbstracteLaag.GetInstance().Categoriën.Contains(poi.Artikelen[i].Categorie)))
                            {
                                poi.Artikelen.RemoveAt(i);
                            }
                        }
                    }
                    int aantal = poi.Artikelen.Count;
                    //als winkelwagen vol is, de maximale afstand gelopen is
                    if (!kar.Vol && (kar.Afstand <= AbstracteLaag.GetInstance().AantalMeter))
                    {
                        List<Winkelwagen> wagens = new List<Winkelwagen>();
                        if (!kar.Plaats.Doorlopen)
                        {
                            #region alle Combinaties van Artikelen worden hier gemaakt
                            for (int i = (int)Math.Pow(2, aantal); i >= 0 ; i--)
                            {
                                Winkelwagen tem = new Winkelwagen(kar);
                                wagens.Add(tem);
                                int plaats = Math.Abs((int)Math.Pow(2, aantal) - i);
                                int temp = i;
                                for (int j = 0; j < aantal; j++)
                                {
                                    if (temp % 2 == 1)
                                    {
                                        wagens[plaats].LaadArtikelIn(poi.Artikelen[j]);
                                    }
                                    if (temp != 0)
                                    {
                                        temp = temp / 2;
                                    }
                                }
                                if (kar.Afstand == AbstracteLaag.GetInstance().AantalMeter)
                                {
                                    antwoord.Add(new Oplossing(Toestand, wagens[plaats]));
                                }
                            }
                            kar.Plaats.Doorlopen = true;
                            #endregion
                        }

                        int meters = AbstracteLaag.GetInstance().AantalMeter;
                        #region alle wegen doorlopen
                        if (!kar.Vol && kar.Afstand < meters)
                        {
                            for (int i = 0; i < kar.Plaats.Kinderen.Count; i++)
                            {

                                for (int j = 0; j < wagens.Count; j++)
                                {
                                    Winkelwagen g = new Winkelwagen(wagens[j]);
                                    g.Afstand += kar.Plaats.Kinderen[i].Afstand;
                                    g.Plaats = new POI(toestand[Kar.Plaats.Kinderen[i].Nummer]);
                                    List<POI> toestandkloon = new List<POI>();
                                    toestandkloon.AddRange(Toestand);
                                    Oplossing kind = new Oplossing(toestandkloon, g);
                                    antwoord.Add(kind);
                                }
                            }
                        }
                        #endregion

                    }

                }
                return antwoord;
            }


            public Winkelwagen Kar
            {
                get { return kar; }
                set { kar = value; }
            }

            public List<POI> Toestand
            {
                get { return toestand; }
            }
        }
    }
    
}
